/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "./DirectX9Private.h"

#include "alkes/directx9/DirectXImageBuffer.h"

namespace alkes {

DirectXImageBuffer* DirectXImageBuffer::create()
{
    return new DirectXImageBuffer();
}

DirectXImageBuffer* DirectXImageBuffer::create(IDirect3DDevice* device, uint16_t width, uint16_t height, PixelFormat format)
{
    DirectXImageBuffer* ptr = DirectXImageBuffer::create();

    if (!ptr)
        return ptr;

    if (!ptr->initialize(device, width, height, format))
    {
        ptr->destroy();
        ptr = 0;
    }

    return ptr;
}

DirectXImageBuffer* DirectXImageBuffer::create(IDirect3DTexture* texture, uint16_t image_width, uint16_t image_height)
{
    DirectXImageBuffer* ptr = DirectXImageBuffer::create();

    if (!ptr)
        return ptr;

    if (!ptr->initialize(texture, image_width, image_height))
    {
        ptr->destroy();
        ptr = 0;
    }

    return ptr;
}

void DirectXImageBuffer::destroy()
{
    delete this;
}

DirectXImageBuffer::DirectXImageBuffer()
: ImageBuffer(false)
, texture_(0)
, image_width_(0)
, image_height_(0)
{
}

DirectXImageBuffer::~DirectXImageBuffer()
{
    if (texture_)
    {
        texture_->Release();
    }
}

bool DirectXImageBuffer::initialize(IDirect3DDevice* device, uint16_t width, uint16_t height, PixelFormat format)
{
    D3DFORMAT dxformat = alformat2dxformat(format);

    if (D3DFMT_UNKNOWN == dxformat)
        return false;

    bool ret = false;
    IDirect3DTexture* texture;
    if (!FAILED(AL_DX9_INVOKE(device->CreateTexture(
            width, height,
            1,
            0/*D3DUSAGE_DYNAMIC*/,
            dxformat,
            D3DPOOL_MANAGED,
//            D3DPOOL_DEFAULT,
//            D3DPOOL_SYSTEMMEM,
            &texture,
            NULL))))
    {
        texture_ = texture;
        image_width_ = width;
        image_height_ = height;
        ret = true;
    }

    return ret;
}

bool DirectXImageBuffer::initialize(IDirect3DTexture* texture, uint16_t image_width, uint16_t image_height)
{
    AL_PRE_COND(texture_ == 0 && texture != 0, false);

    texture_ = texture;
    image_width_ = image_width;
    image_height_ = image_height;

    return true;
}

void DirectXImageBuffer::getSize(uint16_t& width, uint16_t& height) const
{
    D3DSURFACE_DESC desc;
    if (texture_ && !FAILED(texture_->GetLevelDesc(0, &desc)))
    {
        width = (uint16_t)desc.Width;
        height = (uint16_t)desc.Height;
    }
    else
    {
        width = 0;
        height = 0;
    }
}

void DirectXImageBuffer::getImageSize(uint16_t& width, uint16_t& height) const
{
    width = image_width_;
    height = image_height_;
}

PixelFormat DirectXImageBuffer::getPixelFormat() const
{
    if (!texture_)
        return false;

    D3DSURFACE_DESC desc;
    bool ret = !FAILED(texture_->GetLevelDesc(0, &desc));

    return ret ? dxformat2alformat(desc.Format) : AL_PIXEL_FORMAT_UNKNOWN;
}

void* DirectXImageBuffer::onLockRect(const Rect& rect, uint32_t lock_mode, int32_t* pitch)
{
    void* ptr = 0;

    if (texture_)
    {
        DWORD flag = (AL_LOCKMODE_WRITE & lock_mode) ? D3DLOCK_DISCARD : D3DLOCK_READONLY;
        D3DLOCKED_RECT locked;
        HRESULT res;

        RECT dxrect;
        dxrect.left = rect.x;
        dxrect.top = rect.y;
        dxrect.right = rect.x + rect.width;
        dxrect.bottom = rect.y + rect.height;
        res = texture_->LockRect(0, &locked, &dxrect, flag);

        if (!FAILED(res))
        {
            if (pitch)
                *pitch = locked.Pitch;

            ptr = locked.pBits;
        }
    }

    return ptr;
}

void DirectXImageBuffer::onUnlockRect(const Rect& /*locked_rect*/)
{
    if (texture_)
    {
        texture_->UnlockRect(0);
    }
}

}
